package nc.impl.so.m4331;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.BeanUtils;

import io.vavr.collection.Stream;
import nc.bs.dao.BaseDAO;
import nc.bs.dao.DAOException;
import nc.bs.framework.common.NCLocator;
import nc.bs.logging.Logger;
import nc.bs.pub.pf.PfUtilTools;
import nc.bs.pub.util.BaseDaoUtil;
import nc.itf.bd.pubinfo.IPubinfoService;
import nc.itf.so.m4331.IDeliveryMaintain;
import nc.jdbc.framework.processor.BeanProcessor;
import nc.jdbc.framework.processor.ColumnProcessor;
import nc.jdbc.framework.processor.MapListProcessor;
import nc.pubitf.so.m30.api.ISaleOrderQueryAPI;
import nc.vo.am.common.util.CollectionUtils;
import nc.vo.bd.address.AddressVO;
import nc.vo.bd.cust.CustomerVersionVO;
import nc.vo.pub.BusinessException;
import nc.vo.pub.lang.UFBoolean;
import nc.vo.pub.lang.UFDouble;
import nccloud.bs.excel.IXChangeContext;
import nccloud.bs.excel.plugin.AbstractImportProceeWithContext;
import nccloud.pubimpl.so.delivery.event.body.TotalAfterRule;
import nc.vo.so.m30.entity.SaleOrderVO;
import nc.vo.so.m4331.entity.DeliveryBVO;
import nc.vo.so.m4331.entity.DeliveryHVO;
import nc.vo.so.m4331.entity.DeliveryVO;
import nc.vo.so.m4331.pub.DeliveryVOCalculator;

 /**
 * 发货单批量导入
 * 1.校验逻辑
 * 2.有无需要赋初始值
 * 3.保存到数据库
 */
public class DeliveryBatchImport extends AbstractImportProceeWithContext{
	
	/**
	 * arg0
	 * context:可获取前端传过来的参数
	 */
	@Override
	protected void processBillWithContext(Object vo, IXChangeContext context) throws BusinessException {
		// TODO Auto-generated method stub
		String billtype = context.getSwapContext().getBilltype();
		String appcode = context.getXChangeConfigInfo().getPagetemplet().getAppcode();
		DeliveryVO delivery = (DeliveryVO)vo;
		if(null != delivery) {
			// 导入前根据销售单号 生成送货单
			DeliveryVO _delivery = getDefaultDelivery(delivery, billtype);
			// 组装送货单，每一个发货地址组成一张送货单
			DeliveryVO[] insertvos = insertVos(_delivery,delivery);
			if (insertvos != null && insertvos.length > 0) {// 保存数据
				DeliveryVO[] _insertvos = megerBill1(insertvos);
				dealWithPriceTaxamount(_insertvos); // 计算含税不含税金额及价税合计等字段
				IDeliveryMaintain deliveryMaintain = (IDeliveryMaintain)NCLocator.getInstance().lookup(IDeliveryMaintain.class);
				deliveryMaintain.insertDelivery(_insertvos);
			}
		}
		
	}
	/**
	 * 合并单据，合并条件 当子表行上的 发货仓库和发货地址是相同时  合并数量，并删除多余的单据
	 * @param bills
	 * @return
	 */
	private DeliveryVO[] megerBill1(DeliveryVO[] bills) {
		Map<String, List<DeliveryBVO>> deliverybMap = 
				Stream.of(bills).flatMap(b->Stream.of(b.getChildrenVO())).collect(Collectors.groupingBy(b->b.getCsendstordocid()+"@" + b.getVbdef8()));		
		List<DeliveryVO> finalbills = new ArrayList<>();
		deliverybMap.forEach((k,v)->{
			List<DeliveryBVO> bvos = v;
			if(bvos == null) {
				return;
			}
			List<DeliveryBVO> finals  = new ArrayList<>();
			Map<String, List<DeliveryBVO>> bvoMaterialMap = bvos.stream().collect(Collectors.groupingBy(DeliveryBVO::getCmaterialvid));
			bvoMaterialMap.forEach((key,values)->{
				UFDouble sumqty = new UFDouble();
				for(DeliveryBVO b : values) {
					sumqty = sumqty.add(b.getNastnum());
				}
				DeliveryBVO deliveryBVO = (DeliveryBVO)values.get(0).clone();
				deliveryBVO.setNastnum(sumqty);
				finals.add(deliveryBVO);
			});
			DeliveryVO bill = new DeliveryVO();
			bill.setParentVO((DeliveryHVO)bills[0].getParentVO().clone());
			bill.setChildrenVO(finals.toArray(new DeliveryBVO[finals.size()]));
			finalbills.add(bill);
		});
		for(DeliveryVO dvo:finalbills) {
			if(dvo.getChildrenVO().length >0) {
				for(DeliveryBVO dbvo:dvo.getChildrenVO()) {
					dbvo.setVbdef8(null); // 清空暂存的发货地址
					dbvo.setVbdef9(null); // 清空暂存的销售订单
				}
			} 
		}
		return finalbills.toArray(new DeliveryVO[finalbills.size()]);
	}
	

	 
	/**
	 * 根据数量计算相关字段
	 * @param insertvos
	 */
	private void dealWithPriceTaxamount (DeliveryVO[] insertvos) {
		for(DeliveryVO vo:insertvos) {
			DeliveryVOCalculator cal = new DeliveryVOCalculator(vo.getChildrenVO());
			int[] indices = new int[vo.getChildrenVO().length];
			for (int i = 0; i < vo.getChildrenVO().length; i++) {
			    indices[i] = i;
			}
			cal.calculate(indices, "nastnum");
		}
		
	}
	/**
	 * 设置初始值， 根据销售单号查出销售单，转送货单
	 * @param delivery
	 * @param billtype
	 */
	private DeliveryVO getDefaultDelivery(DeliveryVO delivery,String billtype) {
		try {
			DeliveryBVO[] deliverybs = delivery.getChildrenVO(); // 获取子表数据
			DeliveryBVO bvo = deliverybs[0]; // 每条数据都一样，取第一条数据
			String billNo = bvo.getVbdef9(); // 销售订单号
			//先根据销售订单号 查询出销售订单id（id 要写sql 查询）  
			ISaleOrderQueryAPI maintainsrv = (nc.pubitf.so.m30.api.ISaleOrderQueryAPI)NCLocator.getInstance().lookup(ISaleOrderQueryAPI.class);
			String pkId = getSalePkByBillCode(billNo); // 销售单id
			SaleOrderVO[] saleOrders = maintainsrv.queryVOByIDs(new String[] {pkId});
			// 查询出的销售订单再作为参数传给runChangeDataAry 方法
			/**
			 * 单据转换，销售单转换为发货单
			 * 第一个参数来源单据，销售单  单据类型编码是 30
			 * 第二个参数是目标单据，也就是要转换为的单据是发货单，单据类型编码为4331 
			 * 第三个参数是 来源单据的实体 （我们要把销售订单转换为发货单，所以来源单据实体就是销售订单）
			 * 返回值是一个数组
			 */
			DeliveryVO[] deliveryVOs = (DeliveryVO[]) PfUtilTools.runChangeDataAry("30", "4331", saleOrders);
			DeliveryVO vo = deliveryVOs[0]; /// 取出发货单，开始根据  deliverybs 循环，理论上每一条都是一样的，只有发货地址不一样
			return vo;
		} catch (BusinessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  
		return null;
	}
	// 根据销售单据号获取销售单主键
	private String getSalePkByBillCode(String billCode) throws DAOException {
		BaseDAO dao = new BaseDAO();
		String sql = "select csaleorderid from so_saleorder where vbillcode = '"+billCode+"'";
		Object pk = dao.executeQuery(sql,  new ColumnProcessor());
		return pk==null?"":(String) pk;
	}
	// 根据发货类型主键，获取发货类型编码
	private String getVtrantypecode(String ctrantypeid) throws DAOException {
		BaseDAO dao = new BaseDAO();
		String sql = "select PK_BILLTYPECODE from BD_BILLTYPE where PK_BILLTYPEID = '"+ctrantypeid+"'";
		Object pk = dao.executeQuery(sql,  new ColumnProcessor());
		return pk==null?"":(String) pk;
	}
	/**
	 * 复制source的属性值到target
	 * @param target 数据库生成的
	 * @param source  客户导入的
	 */
	private void copyProperties(DeliveryBVO target,DeliveryBVO source) {
		String[] sourceFields = source.getAttributeNames();//获取源对象的字段名
		for(String field:sourceFields) {
			if(source.getAttributeValue(field) != null) { //字段值不为null 
				target.setAttributeValue(field, source.getAttributeValue(field));
			}
		}
			
	}
	/**
	 * 按照子表的行数拆分单据为多个不同发货地址的送货单
	 * @param vo1 销售单转换为的送货单
	 * @param vo2 客户导入转换为的送货单
	 * @return
	 * 需要补齐相关数据
	 * 先看看销售单转换的单据有哪些值，取出导入的子表的第i条数据 把对应的值赋给转换单据，把转换单据存list
	 */
	private DeliveryVO[] insertVos(DeliveryVO vo1,DeliveryVO vo2) throws BusinessException {
		DeliveryHVO parentVO = vo1.getParentVO();
		DeliveryHVO parentVO2 = vo2.getParentVO();
		//主表以下三个字段用用户导入的数据	
		parentVO.setDbilldate(parentVO2.getDbilldate()); // 单据日期
		parentVO.setCtrantypeid(parentVO2.getCtrantypeid()); // 发货类型
		String vtrantypecode = getVtrantypecode(parentVO2.getCtrantypeid());// 获取发货类型编码
		parentVO.setVtrantypecode(vtrantypecode);// 发货类型编码
		parentVO.setVdef3(parentVO2.getVdef3());// 合同号
		parentVO.setStatus(2); // 设置状态为2 表示插入
		parentVO.setVdef13(parentVO2.getVdef13()); // 出库备注
		parentVO.setVdef16(parentVO2.getVdef16()); // PI号
		parentVO.setVdef2(parentVO2.getVdef2()); // 境内境外
		parentVO.setVdef21(parentVO2.getVdef21()); // 是否随货开票
		parentVO.setCtransporttypeid(parentVO2.getCtransporttypeid()); // 运输方式
		parentVO.setVnote(parentVO2.getVnote()); // 备注
		parentVO.setVdef20(parentVO2.getVdef20()); // MES同步状态
		DeliveryBVO[] bvos1 = vo1.getChildrenVO(); //获取生成子表数据
		DeliveryBVO[] bvos2 = vo2.getChildrenVO(); // 获取导入子表数据
		splitAddress(bvos2);//每一条子表数据 要把发货地址拆分给国家省市区街道详细地址，收货人，联系电话并保存到相应数据
		List<DeliveryVO> insertVos = new ArrayList<DeliveryVO>();
		/**
		 * 销售订单会有多个物料 所以详细信息会有多行
		 * 此时需要根据物料代码匹配客户导入的数据
		 * 两个循环，关联字段是物料编码
		 */
		
		BaseDAO dao =new BaseDAO();
		List<String> vo1CustVidList = java.util.stream.Stream.of(bvos1).filter(b->StringUtils.isNotBlank(b.getCordercustvid())).map(DeliveryBVO::getCordercustvid).distinct().collect(Collectors.toList());
		Map<String,String> stockOrgIdBvos1Map =java.util.stream.Stream.of(bvos1).collect(Collectors.toMap(b->{
			StringBuilder sb = new StringBuilder();
			sb.append(b.getVfirstcode()).append("@").append(b.getCordercustvid()).append("@").append(b.getCmaterialvid());
			return sb.toString();
		}, v->v.getCsendstockorgid(),(k1,k2)->k1));
		
		for(DeliveryBVO bvo2 : bvos2) {
			//增加导入的客户是否存在销售订单上
			if(!vo1CustVidList.contains(bvo2.getCordercustvid())) {
				CustomerVersionVO customerVersionVO =(CustomerVersionVO)dao.executeQuery("select * from bd_customer_v where pk_customer_v = '"+bvo2.getCordercustvid()+"'", new BeanProcessor(CustomerVersionVO.class));
				throw new BusinessException(String.format("客户编码:%s在销售订单:%s上不存在！", customerVersionVO.getCode(),bvo2.getVbdef9()));
			}
			StringBuilder sb = new StringBuilder();
			sb.append(bvo2.getVbdef9()).append("@").append(bvo2.getCordercustvid()).append("@").append(bvo2.getCmaterialvid());
			String key = sb.toString();
			if(stockOrgIdBvos1Map.containsKey(key)) {
				//根据发货组织与仓库名称重新查询发货仓库
				String stockOrgId = stockOrgIdBvos1Map.get(key);
				String stordocId = (String)dao.executeQuery("SELECT bs.PK_STORDOC FROM BD_STORDOC bs WHERE bs.name IN (SELECT bs2.name FROM BD_STORDOC bs2 WHERE bs2.PK_STORDOC = '"+bvo2.getCsendstordocid()+"') AND bs.PK_ORG = '"+stockOrgId+"'", new ColumnProcessor());
				if(StringUtils.isBlank(stordocId)) {
					String stockName = (String)dao.executeQuery("SELECT bs2.name FROM BD_STORDOC bs2 WHERE bs2.PK_STORDOC = '"+bvo2.getCsendstordocid()+"'", new ColumnProcessor());
					throw new BusinessException(String.format("根据销售订单:%s的发货库存组织查不到该仓库:%s,请检查仓库名称是否正确!",bvo2.getVbdef9(), stockName));
				}
				bvo2.setCsendstordocid(stordocId);
			}
		}
		
		for (DeliveryBVO bvo1 : bvos1) {
			//提取赠品字段出来,避免被覆盖
			UFBoolean blargessflag = bvo1.getBlargessflag();
			for(DeliveryBVO bvo2 : bvos2) {
				// 生成的子表的物料编码等于导入的子表的物料编码 那么把导入的信息set到导入生成的行上
				if(bvo1.getCmaterialvid().equalsIgnoreCase(bvo2.getCmaterialvid())) { 
					DeliveryVO insertVo = new DeliveryVO();
					// 主表 要克隆一遍，否则认为是同一个对象，同一个地址
					insertVo.setParentVO((DeliveryHVO)parentVO.clone()); // 设置主表
					copyProperties(bvo1,bvo2); // 复制bvo2不为空的属性到
					bvo1.setBlargessflag(blargessflag);
					bvo1.setCmaterialvid(bvo2.getCmaterialvid());// 物料编码
					bvo1.setCastunitid(bvo2.getCastunitid());// 单位
					bvo1.setNastnum(bvo2.getNastnum());//数量
					//发货地址  联系人 联系电话 赋值为自定义项345
					bvo1.setVbdef3(bvo2.getVbdef3());// 发货地址 
					bvo1.setVbdef4(bvo2.getVbdef4());// 联系人
					bvo1.setVbdef5(bvo2.getVbdef5());// 联系电话
					bvo1.setVbdef7(bvo2.getVbdef7());// 发货备注
					/**
					 * 下面代码根据报价换算率计算主数量
					 * 报价换算率字段的两个数相除
					 * 数量乘以报价换算率得到主数量
					 */
					String vqtunitrate = bvo1.getVqtunitrate();//报价换算率
					String[] _vqtunitrate =vqtunitrate.split("/");
					UFDouble u1 = new UFDouble(_vqtunitrate[0] + "");
					UFDouble u2 = new UFDouble(_vqtunitrate[1] + "");
					UFDouble vqtunitrateD = u1.div(u2).setScale(2, UFDouble.ROUND_HALF_UP);// 报价率相除
					UFDouble nnum = bvo2.getNastnum().multiply(vqtunitrateD); // 数量乘以报价换算率
					bvo1.setNnum(nnum);//主数量nnum
					bvo1.setNqtunitnum(bvo2.getNastnum());//报价数量等于数量
//					bvo1.setCdeliverybid(bvo2.getCdeliverybid());//避免主键重复
					insertVo.setChildrenVO(new DeliveryBVO[] {(DeliveryBVO)bvo1.clone()});//子表，一条数据一张单据  但是这里的数据还是以平台生成的单据为准，把客户导入的数据set 进去
					insertVos.add(insertVo);
				}
			}
		}
		return (DeliveryVO[])insertVos.toArray(new DeliveryVO[insertVos.size()]);
	}
	
	/**
	 * 按空格拆分发货地址，联系人，联系电话
	 * @param vos
	 * @throws BusinessException 
	 */
	private void splitAddress(DeliveryBVO[] vos ) throws BusinessException {
//		HashMap<String,String> map = archivesQuery();
		for (DeliveryBVO bvo : vos) {
			String addressInfo = bvo.getVbdef8();
			//替换网页复制下来的空格转义符NBSP
			if(StringUtils.contains(addressInfo, " ")) {
				addressInfo = StringUtils.replace(addressInfo, " ", " ");
			}
			String[] addressInfos = addressInfo.split(" ");// 根据空格拆分 0:地址 1:联系人 2:联系电话
			bvo.setVbdef3(addressInfo);
			if(addressInfos.length > 1) {
//				String pk_psndoc = map.get(addressInfos[1].trim());
//				bvo.setCsendpersonid(pk_psndoc);// 联系人
				bvo.setVbdef4(addressInfos[1].trim()); // 联系人
			}
			if(addressInfos.length > 2) {
//				bvo.setVsendtel(addressInfos[2].trim());//联系电话
				bvo.setVbdef5(addressInfos[2].trim()); // 联系电话
			}				
		}
	}
	

	/**
	 * @return 返回的map key 是客户导入的值，value 是 数据库里对应的主键
	 * @throws DAOException 
	 */
	private HashMap<String,String> archivesQuery () throws DAOException{
		BaseDAO dao = new BaseDAO();
		ArrayList<Map> personList = (ArrayList<Map>)dao.executeQuery("select name,pk_psndoc from bd_psndoc", new MapListProcessor());
		HashMap<String,String> map = new HashMap();
		personList.stream().forEach(e->{
			map.put(e.get("name").toString(), e.get("pk_psndoc").toString());
		});
		return map;
	}
	//保存地址 返回地址的pk
	private String insertAddress(String addr) throws BusinessException {
		BaseDAO dao = new BaseDAO();
		String sql = " SELECT pk_address FROM BD_ADDRESS WHERE DETAILINFO ='" + addr.trim() + "' ";
		Object pk_address = dao.executeQuery(sql, new ColumnProcessor());
		if(pk_address == null) {
			IPubinfoService addressService = ((IPubinfoService) NCLocator.getInstance().lookup(IPubinfoService.class));
			AddressVO address = new AddressVO();
			address.setDetailinfo(addr.trim());
			address.setStatus(2); // 保存地址时要设置该字段 代表新增
			AddressVO result1 = (AddressVO) addressService.insert(address);
			return result1.getPk_address();
		}
		return (String)pk_address;
	}
}
